package org.bluedream.comm.base;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.bluedream.comm.utils.CreateGUIDUtil;
import org.bluedream.comm.utils.EmptyUtil;
import org.bluedream.comm.utils.ReflexUtil;
import org.bluedream.comm.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.util.*;

@Transactional(
        readOnly = true
)
public class TreeServiceImpl<M extends BaseMapper<T>, T> extends BaseServiceImpl<M , T> {
    protected Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    @Transactional(
            readOnly = false,
            rollbackFor = Exception.class
    )
    public boolean save(T entity) {
        try {
            this.check(entity);

            T parent = (T) ReflexUtil.getFieldValue(entity , "parent");

            if (EmptyUtil.isNoEmpty(parent)){
                //如果存在父级节点，则修改父级节点为 非末级节点
                String parentId = (String) ReflexUtil.getFieldValue(parent , "id");

                T thisParent = EmptyUtil.isNoEmpty(parentId)?super.getById(parentId):null;
                if (EmptyUtil.isNoEmpty(thisParent)){
                    ReflexUtil.setFieldValue(entity , "parent" , thisParent);
                    boolean isTreeLeaf = (boolean)ReflexUtil.invoke(thisParent, "getIsTreeLeaf", null);
                    if (isTreeLeaf){
                        ReflexUtil.invoke(thisParent , "setTreeLeaf" , new Class[]{String.class}, "0");
//                        thisParent.setTreeLeaf("0");
                        super.updateById(thisParent);
                    }
                }
            }

            ReflexUtil.invoke(entity , "setTreeEntityGoal" , null);

            /**
             * 判断是否需要限制tree 层级
             */
            boolean isNewRecord = (boolean)ReflexUtil.invoke(entity , "getIsNewRecord" , null);
            boolean limitTreeLevel = (boolean) ReflexUtil.invoke(entity , "getLimitTreeLevel" , null);
            if (limitTreeLevel){
                int treeLevel = (int) ReflexUtil.invoke(entity , "getTreeLevel" , null) + 1;
                int maxTreeLevel = (int) ReflexUtil.invoke(entity , "getMaxTreeLevel" , null);
                if (maxTreeLevel == 0){
                    throw new RuntimeException("限制层级，但是并未指定系统最大允许级别，请联系管理员！");
                }else if (treeLevel > maxTreeLevel){
                    throw new RuntimeException("当前层级大于系统允许最大层级，请确认！");
                }else if (treeLevel == maxTreeLevel){
                    ReflexUtil.setFieldValue(entity , "treeLeaf" , "1");
                }else if (treeLevel < maxTreeLevel){
                    ReflexUtil.setFieldValue(entity , "treeLeaf" , "0");
                }
            }else if (isNewRecord){
                ReflexUtil.setFieldValue(entity , "treeLeaf" , "1");
            }
            return super.save(entity);
        }catch (Exception e1){
            e1.printStackTrace();
            logger.error("保存失败，错误信息：" + e1.getMessage());
            //todo:@Transactional和try catch捕获异常会让注解失效
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return false;
        }
    }

    /**
     * 检查对象 是否继承了 TreeEntity ，如果父类不包含TreeEntity，则抛出异常
     * @param entity
     * @return
     */
    private boolean check(T entity){
        Class<?> clazz = entity.getClass();
        String className = new TreeEntity<>().getClass().getName();
        while (clazz != null){
            if (clazz.getName().equals(className)){
                return true;
            }
            clazz = clazz.getSuperclass();
        }
        throw new RuntimeException("该对象不符合TreeEntity结构，不能保存！");
    }


    /**
     * 返回父节点 等于 T.getParent 下的最后一个子节点 排序规则："treeSorts" , "treeSort" DESC
     * 如果 父节点下没有子节点数据，则返回 null
     * @param entity
     * @return
     */
    public T getLastChildrenNode(T entity , String... orders){
        Page<T> page = new Page<>(1,1);
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        String parentCode = this.getParentCode(entity);
        queryWrapper.eq("PARENT_CODE" , parentCode);
        queryWrapper.orderByDesc(orders);
        IPage<T> iPage = super.page(page , queryWrapper);
        if (EmptyUtil.isNoEmpty(iPage.getRecords())){
            return iPage.getRecords().get(0);
        }
        return null;
    }

    private String getParentCode(T entity){
        this.check(entity);
        String parentCode = (String) ReflexUtil.getMethodValue(entity , "getParentCode" );
        return EmptyUtil.isNoEmpty(parentCode)?parentCode:"0";
    }

    /**
     * 创建并初始化下一节点信息
     * 0.前端数据仅包含：parentCode、id
     * 1.通过parentCode
     *  1.1 获取父级节点最末节点对象last数据。默认返回null
     *  1.2 获取新的父级节点node对象，默认id = “0”
     *  1.3 如果id ！= null ， 初始化company对象
     * 2.新增节点：初始化treeCode：如果 last不为空，则+1。如果为空，但是父级对象不为空，则父级treeCode+ 001
     * 3.编辑节点：如果父级节点无变化，则不更新treeSort排序数据，否则更新
     * @param entity
     * @return
     */
    public T createNextNode(T entity , String... lastChildrenNodeOrderArgs) {
        try {
            this.check(entity);

            Class clazz = entity.getClass();
            Constructor c = clazz.getConstructor(String.class);
            // 创建 父节点Node对象，默认Id = 0
            T rootTreeEntity = (T) c.newInstance("0");
            String parentCode = this.getParentCode(entity);
            T lastChildrenNode = this.getLastChildrenNode(entity , lastChildrenNodeOrderArgs);
            boolean isNewRecord = (boolean) ReflexUtil.invoke(entity , "getIsNewRecord" , null);

            int _sort = TreeEntity.DEFAULT_TREE_SORT;
            String treeCode = TreeEntity.DEFAULT_TREE_CODE;

            if (!parentCode.equals("0") && EmptyUtil.isNoEmpty(parentCode)){
                rootTreeEntity = this.getById(parentCode);
            }else {
                ReflexUtil.setFieldValue(rootTreeEntity , "treeName" , "");
            }

            /**
             * 分为两种新增：
             * 1.点击新增按钮打开form时，会传入一个空对象
             *  1.1 空对象时，父节点 初始化为 0
             * 2.前端编辑新对象时，传入一个带有参数的对象
             */
            if (!isNewRecord){
                /**
                 * 编辑
                 * 1. 原父节点不变时，直接返回
                 * 2.父节点发生改变
                 */
                treeCode = (String) ReflexUtil.getFieldValue(entity , "treeCode");
                T oldEntity = this.getById((Serializable) ReflexUtil.getFieldValue(entity, "id"));
                String oldParentCode = (String) ReflexUtil.getFieldValue(oldEntity , "parentCode");
                String rootEntityId = TreeEntity.ROOT_CODE;
                if (EmptyUtil.isNoEmpty(parentCode) && !rootEntityId.equals(parentCode)){
                    rootEntityId = (String) ReflexUtil.getFieldValue(rootTreeEntity , "id");
                }
                if (oldParentCode.equals(rootEntityId)){
                    return entity;
                }
            }else {
                if (EmptyUtil.isNoEmpty(lastChildrenNode)){
                    treeCode = CreateGUIDUtil.nextCode((String) ReflexUtil.getFieldValue(lastChildrenNode , "treeCode"));
                }else {
                    treeCode = ReflexUtil.getFieldValue(rootTreeEntity , "treeCode") == null? treeCode:ReflexUtil.getFieldValue(rootTreeEntity , "treeCode") + "001";
                }
            }
            if (EmptyUtil.isNoEmpty(lastChildrenNode)){
                // 生成tree 排序 序号 sort
                _sort = (int)ReflexUtil.getFieldValue(lastChildrenNode , "treeSort") + TreeEntity.TREE_SORTS_LENGTH;

            }
            ReflexUtil.setFieldValue(entity , "parent" , rootTreeEntity);
            ReflexUtil.setFieldValue(entity , "treeSort" , _sort);
            ReflexUtil.setFieldValue(entity , "treeCode" , treeCode);
            return entity;
        }catch (Exception e1){
            e1.printStackTrace();
        }
        return null;
    }

    public int getDefaultTreeSort(T entity , String... lastChildrenNodeOrderArgs){
        T lastChildrenNode = this.getLastChildrenNode(entity , lastChildrenNodeOrderArgs);
        int _sort = TreeEntity.DEFAULT_TREE_SORT;
        if (EmptyUtil.isNoEmpty(lastChildrenNode)){
            // 生成tree 排序 序号 sort
            _sort = (int)ReflexUtil.getFieldValue(lastChildrenNode , "treeSort") + TreeEntity.TREE_SORTS_LENGTH;

        }
        return _sort;
    }

    /**
     * 获取树结构数据
     * @param excludeCode 排除的code
     * @param isShowCode 是否显示编码（true or 1：显示在左侧；2：显示在右侧；false or null：不显示）
     * @return
     */
    public List<Map<String , Object>> treeData(String excludeCode , String isShowCode , T entity) {
        try {
            return treeData(excludeCode , isShowCode , entity , null , null);
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取树结构数据
     * @param isShowCode 是否显示编码（true or 1：显示在左侧；2：显示在右侧；false or null：不显示）
     * @return
     */
    public List<Map<String , Object>> treeData(String isShowCode , T entity , Map<String , Object> search) {
        try {
            return treeData("" , isShowCode , entity , null , search);
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取树结构数据
     * @param isShowCode 是否显示编码（true or 1：显示在左侧；2：显示在右侧；false or null：不显示）
     * @return
     */
    public List<Map<String , Object>> treeData(String isShowCode , T entity ) {
        try {
            return treeData("" , isShowCode , entity , null , null);
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取树结构数据
     * @param isShowCode 是否显示编码（true or 1：显示在左侧；2：显示在右侧；false or null：不显示）
     * @return
     */
    public List<Map<String , Object>> treeData(String excludeCode , String isShowCode , T entity , Map<String , Object> search) {
        try {
            return treeData(excludeCode , isShowCode , entity , null , search);
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取树结构数据
     * @param excludeCode 排除的code
     * @param isShowCode 是否显示编码（true or 1：显示在左侧；2：显示在右侧；false or null：不显示）
     * @return
     */
    public List<Map<String , Object>> treeData(String excludeCode , String isShowCode , T entity , List<T> isChecked) {
        try {
            return treeData(excludeCode , isShowCode , entity , isChecked , null);
        }catch (Exception e1){
            e1.printStackTrace();
        }
        return null;
    }

    /**
     * 返回该实体类物理表中最大层级
     * @param entity
     * @return
     */
    private int getMaxTreeLevel(T entity){
        QueryWrapper<T> queryWrapper = new QueryWrapper<T>(entity);
        List<T> list = super.list(queryWrapper);
        int maxLevel = 0;
        for (int i = 0; i < list.size(); i++) {
            int _treeLevel = ((TreeEntity)list.get(i)).getTreeLevel();
            if (_treeLevel > maxLevel){
                maxLevel = _treeLevel;
            }
        }
        return maxLevel;
    }

    /**
     * 该方法用于构建 order by 排序条件，返回 字符串类型，如果用@select注解构建order by时，注意要使用 ${orderByStr} 传入
     * 当treeData的实体类排序字段为此类结构时：10,20,30,  此时排序可能存在问题
     * to_number(REGEXP_SUBSTR(tree_sorts, '[^,]+', 1, " + (i + 1) + ")) NULLS FIRST 可以拆分该字段
     * @param entity
     * @return
     */
    public String orderByString(T entity){
        int maxLevel = getMaxTreeLevel(entity);
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i <= maxLevel ; i++) {
            if (i == 0){
                sb.append("to_number(REGEXP_SUBSTR(tree_sorts, '[^,]+', 1, " + (i + 1) + ")) NULLS FIRST");
            }else {
                sb.append(",to_number(REGEXP_SUBSTR(tree_sorts, '[^,]+', 1, " + (i + 1) + ")) NULLS FIRST");
            }
        }
        return sb.toString();
    }

    /**
     *
     * @param excludeCode
     * @param isShowCode
     * @param source
     * @param isChecked
     * @return
     */
    public List<Map<String , Object>> treeData(String excludeCode , String isShowCode , List<T> source , List<T> isChecked){
        List<Map<String , Object>> mapList = new ArrayList<>();
        treeDataIsChecked(excludeCode, isShowCode, (List<T>) source, (List<T>) isChecked, mapList);
        return mapList;
    }

    public void treeDataIsChecked(String excludeCode, String isShowCode, List<T> source, List<T> isChecked, List<Map<String, Object>> mapList) {
        for (T c1 : source
        ) {
            if (!BaseEntity.STATUS_NORMAL.equals(
                    ReflexUtil.getFieldValue(c1, "status")
            )) {
                continue;
            }
            if (EmptyUtil.isNoEmpty(excludeCode)) {
                if (ReflexUtil.getFieldValue(c1, "id").equals(excludeCode)) {
                    continue;
                }
                String parentCodes = (String) ReflexUtil.getFieldValue(c1, "parentCodes");
                if (parentCodes.contains("," + excludeCode + ",")) {
                    continue;
                }
            }
            Map<String, Object> map = new HashMap<>();
            String id = (String) ReflexUtil.getFieldValue(c1, "id");
            if (EmptyUtil.isNoEmpty(isChecked)) {
                for (T c2 : isChecked
                ) {
                    String c2Id = (String) ReflexUtil.getFieldValue(c2, "id");
                    if (id.equals(c2Id)) {
                        map.put("isChecked", true);
                    }
                }
            }

            map.put("id", id);
            map.put("pId", ReflexUtil.getFieldValue(c1, "parentCode"));
            map.put("code", ReflexUtil.getFieldValue(c1, "treeCode"));
            map.put("name", StringUtils.getTreeNodeName(isShowCode, (String) ReflexUtil.getFieldValue(c1, "treeCode"), (String) ReflexUtil.getFieldValue(c1, "treeName")));
            map.put("treeSorts", ReflexUtil.getFieldValue(c1, "treeSorts"));
            map.put("isTreeLeaf", "1".equals(ReflexUtil.getFieldValue(c1, "treeLeaf")));
            map.put("isParent", "0".equals(ReflexUtil.getFieldValue(c1, "treeLeaf")));
            map.put("isRoot", "0".equals(ReflexUtil.getFieldValue(c1, "treeCode")));
            mapList.add(map);
        }
    }

    public List<Map<String , Object>> treeData(String excludeCode , String isShowCode , T entity , QueryWrapper<T> queryWrapper){
        try{
            List<Map<String , Object>> mapList = new ArrayList<>();
            List<T> list = super.list(queryWrapper);
            treeDataIsChecked(excludeCode, isShowCode, list, null, mapList);
            return mapList;
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    public List<Map<String , Object>> treeData(String excludeCode , String isShowCode , T entity , List<T> isChecked , Map<String , Object> search){
        try {
            List<Map<String , Object>> mapList = new ArrayList<>();
            QueryWrapper<T> queryWrapper = new QueryWrapper<T>(entity);
            queryWrapper.eq("1" , "1");
            if (EmptyUtil.isNoEmpty(search)){
                queryWrapperIsInput(search, queryWrapper);
            }else {
                queryWrapper = new QueryWrapper<>(entity);
            }
            queryWrapper.last("order by " + this.orderByString(entity));
            List<T> list = super.list(queryWrapper);

//            list = convertToOrder(list);
            treeDataIsChecked(excludeCode, isShowCode, list, isChecked, mapList);
            return mapList;
        }catch (Exception e1){
            e1.printStackTrace();
        }
        return null;
    }

    public void queryWrapperIsInput(Map<String, Object> search, QueryWrapper<T> queryWrapper) {
        for (Map.Entry<String , Object> entry:search.entrySet()
        ) {
            String[] key = entry.getKey().split(";");
            if (key.length > 1){
                if (key[1].equals("eq")){
                    queryWrapper.eq(key[0] , entry.getValue());
                }else if (key[1].equals("ge")){
                    queryWrapper.ge(key[0] , entry.getValue());
                }else if (key[1].equals("lt")){
                    queryWrapper.lt(key[0] , entry.getValue());
                }else if (key[1].equals("like")){
                    queryWrapper.like(entry.getKey() , entry.getValue());
                }
            }else {
                queryWrapper.like(entry.getKey() , entry.getValue());
            }
        }
    }


    public boolean delete(T entity){
        String id = (String) ReflexUtil.getFieldValue(entity , "id");
        String parentCodes = (String) ReflexUtil.getFieldValue(entity , "parentCodes") + id;
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        queryWrapper.likeRight("PARENT_CODES" , parentCodes);
        List<T> T_list = super.list(queryWrapper);
        boolean flag = true;
        if (T_list.size() > 0){
            throw new RuntimeException("当前节点已经被引用，不允许被删除！");
        }
        if (flag){
            return super.removeById(id);
        }
        return false;
    }

    private TreeEntity getRootNode(){
        TreeEntity t1 = new TreeEntity("0");
        t1.setParentCode();
        return t1;
    }

}
